#include "sqlite_connect_pool.h"
#include "easylogging++.h"
#include <QSqlQuery>
#include <QSqlError>
#include "global_manager.h"

QMutex             SqliteConnectPool::mutex;
QWaitCondition     SqliteConnectPool::waitConnection;
SqliteConnectPool* SqliteConnectPool::instance = nullptr;

SqliteConnectPool::SqliteConnectPool()
{
    databaseName       = SQLITE_DB_PATH;
    testOnBorrow       = true;
    testOnBorrowSql    = "SELECT 1";
    databaseType       = "QSQLITE";
    maxWaitTime        = 1000;
    waitInterval       = 100;
    maxConnectionCount = 10;
}

QSqlDatabase SqliteConnectPool::createConnection(const QString &connectName)
{
    QSqlDatabase db;

    if (QSqlDatabase::contains(connectName))
    {
        db = QSqlDatabase::database(connectName);

        if (testOnBorrow)
        {
            QSqlQuery query(testOnBorrowSql, db);

            if (query.lastError().type() != QSqlError::NoError && !db.open())
            {
                LOG(ERROR) << "open database error, error info: " << db.lastError().text().toStdString();
                return QSqlDatabase();
            }
        }

        return db;
    }

    db = QSqlDatabase::addDatabase(databaseType, connectName);
    db.setDatabaseName(databaseName);

    if (!db.open())
    {
        LOG(ERROR) << "open database error, error info: " << db.lastError().text().toStdString();
        return QSqlDatabase();
    }

    if (testOnBorrow)
    {
        QSqlQuery query(testOnBorrowSql, db);

        if (query.lastError().type() != QSqlError::NoError && !db.open())
        {
            LOG(ERROR) << "open database error, error info: " << db.lastError().text().toStdString();
            return QSqlDatabase();
        }
    }

    return db;
}


QSqlDatabase SqliteConnectPool::getConnection()
{
    SqliteConnectPool& pool = SqliteConnectPool::getInstance();
    QString connectionName;

    QMutexLocker locker(&mutex);

    int connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size();

    for (int i = 0;
         i < pool.maxWaitTime &&
         pool.unusedConnectionNames.size() == 0 &&
         connectionCount == pool.maxConnectionCount;
         i += pool.waitInterval)
    {
        waitConnection.wait(&mutex, pool.waitInterval);

        connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size();
    }

    if (pool.unusedConnectionNames.size() > 0)
    {
        connectionName = pool.unusedConnectionNames.dequeue();
    }
    else if (connectionCount < pool.maxConnectionCount)
    {
        connectionName = QString("Connection-%1").arg(connectionCount + 1);
    }
    else
    {
        LOG(ERROR) << "cannot create more connections.";
        return QSqlDatabase();
    }

    QSqlDatabase db = pool.createConnection(connectionName);

    if (db.isOpen())
    {
        pool.usedConnectionNames.enqueue(connectionName);
    }

    return db;
}

void SqliteConnectPool::releaseConnection(QSqlDatabase connection)
{
    SqliteConnectPool& pool = SqliteConnectPool::getInstance();
    QString connectionName = connection.connectionName();

    if (pool.usedConnectionNames.contains(connectionName))
    {
        QMutexLocker locker(&mutex);
        pool.usedConnectionNames.removeOne(connectionName);
        pool.unusedConnectionNames.enqueue(connectionName);
        waitConnection.wakeOne();
    }
}

SqliteConnectPool::~SqliteConnectPool()
{
    foreach (QString connectionName, usedConnectionNames)
    {
        QSqlDatabase::removeDatabase(connectionName);
    }

    foreach (QString connectionName, unusedConnectionNames)
    {
        QSqlDatabase::removeDatabase(connectionName);
    }
}

SqliteConnectPool &SqliteConnectPool::getInstance()
{
    /** 双重检查锁定，减少枷锁次数，提高性能 */
    if (nullptr == instance)
    {
        QMutexLocker locker(&mutex);

        if (nullptr == instance)
        {
            instance = new SqliteConnectPool();
        }
    }

    return *instance;
}
